﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace CueCorrector
{
    public class ErrorEventArgs
    {
        public string Message { get; private set; }

        public ErrorEventArgs(string message)
        {
            Message = message;
        }
    }

    public class FileProcessedEventArgs
    {
        public string FileName { get; private set; }
        public bool Changed { get; private set; }

        public FileProcessedEventArgs(string fileName, bool changed)
        {
            FileName = fileName;
            Changed = changed;
        }
    }

    internal class CueProcessor
    {
        public string FolderPath { private get; set; }
        public bool SaveOldFiles { private get; set; }
        public Func<bool> CheckCancellation { private get; set; }
        public Action<int> ReportProgress { private get; set; }
        public bool Stopped { get; private set; }
        public int ChangedFilesCount { get; private set; }

        private int Percentage
        {
            get { return Convert.ToInt32((++_processedFilesCount/(decimal) _allFilesCount)*100); }
        }

        private const string CueFilter = ".cue";

        private readonly HashSet<string> _possibleExtensions = new HashSet<string>
            {
                ".flac",
                ".ape",
                ".wv",
                ".fla",
                ".wav",
                ".mp3",
                ".m4a"
            };

        private int _allFilesCount;
        private int _processedFilesCount;

        private void OnError(ErrorEventArgs e)
        {
            var handler = Error;
            if (handler != null)
            {
                handler(null, e);
            }
        }

        private void OnFileProcessed(FileProcessedEventArgs e)
        {
            var handler = FileProcessed;
            if (handler != null)
            {
                handler(null, e);
            }
        }

        public event ErrorEventHandler Error;
        public event FileProcessedEventHandler FileProcessed;

        public delegate void ErrorEventHandler(object sender, ErrorEventArgs e);

        public delegate void FileProcessedEventHandler(object sender, FileProcessedEventArgs e);

        public void Process()
        {
            if (!string.IsNullOrWhiteSpace(FolderPath) && Directory.Exists(FolderPath))
            {
                Stopped = false;
                _processedFilesCount = 0;
                _allFilesCount = 0;
                ChangedFilesCount = 0;
                CountFiles(FolderPath);
                ProcessFolder(FolderPath);
            }
            else
            {
                OnError(new ErrorEventArgs("Запрошенной директории " + FolderPath + " не существует"));
            }
        }

        private void ProcessFolder(string path)
        {
            if (Stopped)
            {
                return;
            }
            if (CheckCancellation())
            {
                Stopped = true; //стоп рекурсивных вызовов
                return;
            }
            try
            {
                var files = Directory.GetFiles(path).Where(file => file.EndsWith(CueFilter));

                #region processing

                foreach (var filePath in files)
                {
                    if (CheckCancellation())
                    {
                        Stopped = true; //стоп рекурсивных вызовов
                        return;
                    }
                    string line;
                    var sr = new StreamReader(filePath);
                    var newFile = new StringBuilder();
                    var changed = false;
                    while ((line = sr.ReadLine()) != null)
                    {
                        if (line.Length > 4 && line.Substring(0, 4).ToLower() == "file")
                        {
                            var fileName = line;
                            fileName =
                                fileName.Substring(fileName.IndexOf(' '), fileName.Length - fileName.IndexOf(' '))
                                        .Trim();
                            var fileType =
                                fileName.Substring(fileName.LastIndexOf(' '),
                                                   fileName.Length - fileName.LastIndexOf(' ')).Trim();
                            fileName = fileName.Substring(0, fileName.LastIndexOf(' ')).Trim();
                            if (fileName[0] == '"')
                            {
                                fileName = fileName.Substring(1, fileName.LastIndexOf('"') - 1);
                            }
                            if (!File.Exists(path + "\\" + fileName))
                            {
                                fileName = Path.GetFileNameWithoutExtension(fileName);
                                foreach (
                                    var currentFileNameModification in
                                        _possibleExtensions.Select(possibleExtension => fileName + possibleExtension)
                                                           .Where(
                                                               currentFileNameModification =>
                                                               File.Exists(path + "\\" + currentFileNameModification)))
                                {
                                    line = "FILE \"" + currentFileNameModification + "\" " + fileType;
                                    changed = true;
                                    break;
                                }
                            }
                        }
                        newFile.AppendLine(line);
                    }
                    sr.Close();
                    if (changed)
                    {
                        if (SaveOldFiles)
                        {
                            File.Move(filePath, filePath + "Incorrect");
                        }
                        else
                        {
                            File.Delete(filePath); //Стираем старый
                        }
                        File.WriteAllText(filePath, newFile.ToString());
                        ChangedFilesCount++;
                    }
                    OnFileProcessed(new FileProcessedEventArgs(filePath, changed));
                    ReportProgress(Percentage);
                }

                #endregion

                foreach (var directoryPath in Directory.GetDirectories(path))
                {
                    ProcessFolder(directoryPath);
                }
            }
            catch (Exception ex)
            {
                OnError(new ErrorEventArgs("Ошибка обработки директории " + path));
                ReportProgress(Percentage);
            }
        }

        private void CountFiles(string path)
        {
            if (Stopped)
            {
                return;
            }
            if (CheckCancellation())
            {
                Stopped = true; //стоп рекурсивных вызовов
                return;
            }
            try
            {
                var files = Directory.GetFiles(path).Where(file => file.EndsWith(CueFilter));


                _allFilesCount += files.Count();

                foreach (var directoryPath in Directory.GetDirectories(path))
                {
                    CountFiles(directoryPath);
                }
            }
            catch (Exception ex)
            {
            }
        }
    }
}
